#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <asm/uaccess.h>

//4个模拟输入通道
#define ACHANNEL0 0x40
#define ACHANNEL1 0x41
#define ACHANNEL2 0x42
#define ACHANNEL3 0x43

static int major;//设备号
static struct i2c_client *pcf8591_client;//指向I2C客户端的指针，用于和pcf8591芯片通信
static struct class *pcf8591_class;//指向设备类的指针，用于创建设备文件

//函数声明
int pcf8591_open (struct inode *inode, struct file *filp);
ssize_t pcf8591_read (struct file *filp, char __user *buf, size_t len, loff_t *loff);
ssize_t pcf8591_write (struct file *filp, const char __user *buf, size_t len, loff_t *loff);
static int pcf8591_probe (struct i2c_client *client, const struct i2c_device_id *id);
static int pcf8591_remove (struct i2c_client *client);

//操作函数
static struct file_operations pcf8591_fops = {
	.owner = THIS_MODULE,
	.open = pcf8591_open,
	.read = pcf8591_read,
	.write = pcf8591_write,
};

//i2c client
//设备ACPI
static const struct of_device_id pcf8591_of_match[] = 
{
	{.compatible = "philips,pcf8591"},
	{}
};
//I2C设备ID
static const struct i2c_device_id pcf8591_ids[] = 
{
	{"xxxyyyzzz", (kernel_ulong_t)NULL},
	{},
};

//i2c driver
//I2C驱动的属性
static struct i2c_driver i2c_drv =
{
	.driver = {
		.name = "pcf8591",//驱动名称
		.of_match_table = pcf8591_of_match,//ACPI设备树匹配
	},
	.probe = pcf8591_probe,//探测
	.remove = pcf8591_remove,//移除
	.id_table = pcf8591_ids,//I2C设备ID匹配
};

//从指定通道读取模拟值
unsigned char pcf8591_readADC (unsigned char ch)
{
	return i2c_smbus_read_byte_data (pcf8591_client, ch);
}

int pcf8591_open (struct inode *inode, struct file *filp)
{

	return 0;
}
ssize_t pcf8591_read (struct file *filp, char __user *buf, size_t len, loff_t *loff)
{
	unsigned long ret = 0;
	unsigned char data = 255;
	if (buf[0] == 0) {
		data = pcf8591_readADC (ACHANNEL0);
	} else if (buf[0] == 1) {
		data = pcf8591_readADC (ACHANNEL1);
	} else if (buf[0] == 2) {
		data = pcf8591_readADC (ACHANNEL2);
	} else if (buf[0] == 3) {
		data = pcf8591_readADC (ACHANNEL3);
	}
	ret = copy_to_user (buf, &data, 1);
	return (size_t)ret;
}

ssize_t pcf8591_write (struct file *filp, const char __user *buf, size_t len, loff_t *loff)
{
	unsigned char data = 255;
	unsigned long ret = 0;
	ret = copy_from_user (&data, buf, 1);
	i2c_smbus_write_byte_data (pcf8591_client, ACHANNEL0, data);
	return ret;
}

//配置I/O引脚
static int pcf8591_pad_mux (void)
{
	void __iomem * vaddr;
	struct resource *maddr;
	maddr = request_mem_region (0x32b300a8, 16, "null");
	vaddr = ioremap (maddr->start, 8);
	writel (0x044, vaddr);
	writel (0x44, vaddr + 4);

	//将MIO6控制器配置为IIc控制模式
	vaddr = ioremap (0x28021000, 4);
	writel (0, vaddr);
	return 0;
}

static int pcf8591_probe (struct i2c_client *client, const struct i2c_device_id *id)
{
	pcf8591_client = client;
	if(0 == i2c_check_functionality (client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
		return -1;
	}
	//a)注册cdev对象（字符设备）
	major = register_chrdev (0, "dev_pcf8591", &pcf8591_fops);
	//b)设备类和设备文件的自动创建
	pcf8591_class = class_create (THIS_MODULE, "pcf8591");
	device_create (pcf8591_class, NULL, MKDEV(major, 0), NULL, "pcf8591");
	
	//函数调用，配置I/O引脚
	pcf8591_pad_mux();
	return 0;
}

//释放内存、销毁设备和类、注销字符设备
static int pcf8591_remove (struct i2c_client *client)
{
	release_mem_region (0x32b300a8, 16);
	device_destroy (pcf8591_class, MKDEV(major, 0));
	class_destroy (pcf8591_class);
	unregister_chrdev (major, "pcf85910");
	return 0;
}

//注册I2C驱动
static int i2c_dev_init (void)
{
	int err;
	err = i2c_add_driver (&i2c_drv);
	return err;
}

//注销I2C驱动
static void i2c_dev_exit (void)
{
	i2c_del_driver (&i2c_drv);
	return ;
}

module_init (i2c_dev_init);
module_exit (i2c_dev_exit);
MODULE_LICENSE ("GPL");
